package library

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/gogf/gf/os/glog"
)

const (
	get uint8 = iota
	post

	defaultTimeOut = 15 * time.Second
)

// 通用请求/响应(不需要结果的)参数 map
type BodyMap map[string]interface{}

func NewBodyMap(size int) BodyMap {
	return make(BodyMap, size)
}

func (r BodyMap) Set(key string, value interface{}) BodyMap {
	r[key] = value
	return r
}

func (r BodyMap) Marshal() ([]byte, error) {
	return json.Marshal(r)
}

func (r BodyMap) Unmarshal(resp []byte) (BodyMap, error) {
	err := json.Unmarshal(resp, &r)
	return r, err
}

// 获取格式化后的 json, 用于打印
func (r BodyMap) GetFormatJsonForPrint() string {
	b, _ := json.Marshal(r)
	var dst bytes.Buffer
	_ = json.Indent(&dst, b, "", "\t")
	return dst.String()
}

// UnEscape 不专义
func (r BodyMap) UnEscape() string {
	b, _ := r.Marshal()
	return string(b)
}

type HttpClientOption func(h *HttpCli)

// 设置过去时间
func WithHttpCliTimeOut(timeOut time.Duration) HttpClientOption {
	return func(h *HttpCli) {
		h.client.Timeout = timeOut
	}
}

// 会自己释放 Response.Body
type HttpCli struct {
	client *http.Client
	url    string
	header map[string]string
	body   []byte
}

type HttpCliResp struct {
	StatusCode int
	Body       []byte
	Header     map[string][]string
}

func NewHttpCli(opts ...HttpClientOption) *HttpCli {
	obj := &HttpCli{
		client: &http.Client{
			Timeout: defaultTimeOut,
		},
	}

	for _, opt := range opts {
		opt(obj)
	}
	return obj
}

func (h *HttpCli) SetHeader(key, value string) {
	if h.header == nil {
		h.header = make(map[string]string, 5)
	}
	h.header[key] = value
}

func (h *HttpCli) Get(url string) (*HttpCliResp, error) {
	h.url = url
	glog.Infof("method: GET, url: %s, header: %v", h.url, h.header)
	return h.do(get)
}

// 请求体为 []byte 数组, 不能打印请求体
func (h *HttpCli) Post(url string, body []byte) (*HttpCliResp, error) {
	h.url = url
	h.body = body

	glog.Infof("method: POST, url: %s, header: %v", h.url, h.header)
	return h.do(post)
}

func (h *HttpCli) do(actionNum uint8) (*HttpCliResp, error) {
	methodStr := "GET"
	switch actionNum {
	case get:
		methodStr = "GET"
	case post:
		methodStr = "POST"
	}
	request, err := http.NewRequest(methodStr, h.url, bytes.NewReader(h.body))
	if err != nil {
		return nil, err
	}

	for k, v := range h.header {
		request.Header.Set(k, v)
	}
	resp, err := h.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respByte, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return &HttpCliResp{
		StatusCode: resp.StatusCode,
		Body:       respByte,
		Header:     resp.Header,
	}, nil
}

// =========================================封装常用的方法 ============================================

func Get(url string, headers map[string]string) (*HttpCliResp, error) {
	cli := NewHttpCli()
	for k, v := range headers {
		cli.SetHeader(k, v)
	}
	return cli.Get(url)
}

// 请求体为 []byte 数组, 不能打印请求体
func Post(url string, body []byte, headers map[string]string) (*HttpCliResp, error) {
	cli := NewHttpCli()
	for k, v := range headers {
		cli.SetHeader(k, v)
	}
	return cli.Post(url, body)
}
